package com.fzhucc.utils

import cn.hutool.http.ContentType
import cn.hutool.http.HttpRequest
import cn.hutool.http.HttpResponse
import cn.hutool.http.HttpUtil
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.fzhucc.utils.dto.WxToken
import lombok.extern.log4j.Log4j2
import org.slf4j.LoggerFactory
import java.util.*

/**
 * @author KQiang Weng
 * @since 2022/09/13 14:11
 */
@Log4j2
object WxUtils {
    private const val url = "https://api.weixin.qq.com/sns/jscode2session"

    private val logger = LoggerFactory.getLogger(WxUtils::class.java)

//    private val accessTokens = CacheBuilder.newBuilder()
//        .expireAfterWrite(7000, TimeUnit.SECONDS)
//        .build<String, String>()

    @JvmStatic
    fun jscode2session(appId: String, appSecret: String, code: String): WxToken {
        val result: String = HttpUtil.get(
            url, mapOf(
                "appid" to appId,
                "secret" to appSecret,
                "js_code" to code,
                "grant_type" to "authorization_code"
            )
        )
        val node = JSON.parseObject(result)
        if (node.containsKey("errcode")) {
            throw IllegalStateException(node.getString("errmsg"))
        }
        return JSON.parseObject(result, WxToken::class.java)
    }

    @JvmStatic
    private fun getAccessToken(appId: String, appSecret: String): String {
//        return accessTokens.get(appId + appSecret) {
//            val tokenStr = HttpUtil.get(
//                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appId&secret=$appSecret"
//            )
//            val jsonObject = JSONObject.parseObject(tokenStr)
//            jsonObject.getString("access_token")
//        }
        val tokenStr = HttpUtil.get(
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appId&secret=$appSecret"
        )
        val jsonObject = JSONObject.parseObject(tokenStr)
        return jsonObject.getString("access_token")
    }

    /**
     * 获取小程序二维码
     * returnUrl 微信小程序请求路径
     * parameterstr 请求参数值name
     * parameter 请求参数值
     */
    @JvmStatic
    fun generateMpQr(
        appId: String,
        appSecret: String,
        path: String?,
        query: String?,
        env: String = "release",
    ): String {
        val url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(appId, appSecret)
        val body = buildMap {
            if (!path.isNullOrEmpty()) {
                put("page", path)
            }
            if (!query.isNullOrEmpty()) {
                put("scene", query)
            }
            put("check_path", false)
            put("env_version", env)
            //正式版为 release，体验版为 trial，开发版为 develop
        }
        val response = HttpUtil.createPost(url)
            .body(JSON.toJSONString(body), ContentType.JSON.value)
            .execute()
        if (response.header("Content-Type").contains("json")) {
            val jo = JSON.parseObject(response.body())
            throw IllegalStateException(jo.getString("errmsg"))
        }
        logger.info("Header: " + response.headers())
        return "data:image/jpg;base64," + Base64.getEncoder().encodeToString(response.bodyBytes())
    }


    /**
     * 获取小程序二维码 永久 无参 参数在path上
     * returnUrl 微信小程序请求路径
     * parameterstr 请求参数值name
     * parameter 请求参数值
     */
    @JvmStatic
    fun generateMpQrWithNoParm(
        appId: String,
        appSecret: String,
        path: String?,
    ): String {
        val url = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=" + getAccessToken(appId, appSecret)
        val body = buildMap {
            if (!path.isNullOrEmpty()) {
                put("page", path)
            }
        }
        val response = HttpUtil.createPost(url)
            .body(JSON.toJSONString(body), ContentType.JSON.value)
            .execute()
        if (response.header("Content-Type").contains("json")) {
            val jo = JSON.parseObject(response.body())
            throw IllegalStateException(jo.getString("errmsg"))
        }
        logger.info("Header: " + response.headers())
        return "data:image/jpg;base64," + Base64.getEncoder().encodeToString(response.bodyBytes())
    }

    /**
     * 获取scheme码
     *
     * @param path 通过scheme码进入的小程序页面路径，必须是已经发布的小程序存在的页面，
     *             不可携带query。path为空时会跳转小程序主页。
     * @param query 通过 scheme 码进入小程序时的 query，最大1024个字符，只支持数字，
     *              大小写英文以及部分特殊字符：`!#$&'()*+,/:;=?@-._~%``
     * @param expireInMill 过期时间，单位:毫秒
     * @return 生成的小程序 scheme 码
     */
    @JvmStatic
    fun generateScheme(
        appId: String,
        appSecret: String,
        path: String?,
        query: String?,
        expireInMill: Long,
        env: String = "release",
    ): String {
        val json = postJson(
            "https://api.weixin.qq.com/wxa/generatescheme?access_token=${getAccessToken(appId, appSecret)}",
            buildMap {
                put("jump_wxa", buildMap {
                    if (!path.isNullOrEmpty()) {
                        put("path", path)
                    }
                    if (!query.isNullOrEmpty()) {
                        put("query", query)
                    }
                    put("env_version", env)
                })
                put("is_expire", true)
                put("expire_time", (System.currentTimeMillis() + expireInMill) / 1000)
            }
        )
        val jo = JSON.parseObject(json)
        if (jo.getIntValue("errcode") != 0) {
            throw IllegalStateException(jo.getString("errmsg"))
        }
        return jo.getString("openlink")
    }

    /**
     * 获取小程序 URL Link
     *
     * @param path 通过scheme码进入的小程序页面路径，必须是已经发布的小程序存在的页面，
     *             不可携带query。path为空时会跳转小程序主页。
     * @param query 通过 scheme 码进入小程序时的 query，最大1024个字符，只支持数字，
     *              大小写英文以及部分特殊字符：`!#$&'()*+,/:;=?@-._~%``
     * @param expireInMill 过期时间，单位:毫秒
     * @return 生成的小程序 URL Link
     */
    @JvmStatic
    fun generateUrlLink(
        appId: String,
        appSecret: String,
        path: String?,
        query: String?,
        expireInMill: Long,
        env: String = "release",
    ): String {
        val json = postJson(
            "https://api.weixin.qq.com/wxa/generate_urllink?access_token=${getAccessToken(appId, appSecret)}",
            buildMap {
                if (!path.isNullOrEmpty()) {
                    put("path", path)
                }
                if (!query.isNullOrEmpty()) {
                    put("query", query)
                }
                put("is_expire", true)
                put("expire_type", 0)
                put("expire_time", (System.currentTimeMillis() + expireInMill) / 1000)
                put("env_version", env)
            }
        )
        val jo = JSON.parseObject(json)
        if (jo.getIntValue("errcode") != 0) {
            logger.error(json)
            throw IllegalStateException(jo.getString("errmsg"))
        }
        return jo.getString("url_link")
    }

    private fun postJson(url: String, body: Map<String, Any?>): String {
        return HttpUtil.createPost(url)
            .body(JSON.toJSONString(body), ContentType.JSON.value)
            .execute()
            .body()
    }

    /**
     * 获取手机号
     */
    @JvmStatic
    fun getPhone(
        appId: String,
        appSecret: String,
        code: String
    ): String {
        val json = postJson(
            "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${getAccessToken(appId, appSecret)}",
            buildMap {
                put("code", code)
            }
        )
        val jo = JSON.parseObject(json)
        val jSONObject = jo.getJSONObject("phone_info")
        return jSONObject.getString("phoneNumber")
    }
}